/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "glheaders.h"

#include <assert.h>
#include <string.h>
#include <math.h>
#include "renderUtils.h"
#include "mouseUtils.h"
#include "graph.h"
#include "view.h"
#include "ui/uiUtils.h"
#include "controller.h"
#include <sstream>

/*****************************
LEGENDS
*****************************/
class Legend;
class ColorControl: public Clickable {
public:
    ColorItem *node;
    ColorMap *tf;
    Legend *editor;
    float button1x, button1y, button1w, button1h;
    float button2x, button2y, button2w, button2h;

    ColorControl(ColorItem *node, ColorMap *tf, Legend *editor);
    virtual void draw();
    virtual void drag(float , float , float dx, float dy, int modifiers);
    virtual void click(float x , float y, int modifiers);
};

class Legend: public View {
    string colormapName;
    ColorMap * colormap;
public:
    float colorX, colorScaleX;
    float colorY, colorScaleY;
    float colorControlX, colorControlScaleX;
    float minv, maxv;
    string type;
    Legend(const char *title, string type="node") : View("Color Legend", title) {
				this->type = type;
        colormapName = "";
        colormap = 0;
				setPropertyStringList("Data.Colormap", Controller::addVirtualPrefix(type+".colormap"), "colormap");
				setPropertyStringList("Data.Property", Controller::addVirtualPrefix(type+".color"), type);
        setDefaultSelection(false);
        setMargins(140, 10, 40, 40);
        setZoomable(false);
//      moved to View
//        setPropertyInt("Font Size",10);
//        setPropertyStringList("Font", "Helvetica","font");
    }

    void deleteColorControl(ColorControl *color) {
        assert(colormap);
        deleteClickable(color);
        colormap->deleteColor(color->node);
        colormap->generate();
    }

    void setupControls() {
        if(!colormap) return;
        assert(colormap);
        viewClickables.clear();

        for(unsigned int i = 0; i < colormap->colors.size(); i++) {
            ColorControl *c = new ColorControl(colormap->colors[i], colormap, this);
            addViewClickable(c);
        }
    }

    virtual void click(float x, float y, int modifiers) {
	View::click(x,y,modifiers);
	if(!colormap) return;
        float rx, ry;
        toView(x,y,rx,ry);
        //printf("Click Status = %d\n", getClickStatus());
        if(getClickStatus()==0 && rx>colorX && rx<colorX+colorScaleX) {
            float value = (ry - colorY)/colorScaleY;
            float rgb[3];
            colormap->getColor(value, rgb);
            ColorItem *color = colormap->insert(value, rgb[0], rgb[1], rgb[2]);
            colormap->generate();
            ColorControl *c = new ColorControl(color, colormap, this);
            addViewClickable(c);
            c->setSelected(true);
        } else {
        }
    }

    float getValue(float t) {
        return minv + t*(maxv-minv);
    }

    void render() {
	string nodeProperty = PROPERTY(getPropertyString("Data.Property"));
        graph->getStatManager()->getMinMaxFloat(nodeProperty, minv, maxv);

        View::render();
        string cmap = PROPERTY(getPropertyString("Data.Colormap"));
        if(cmap!=colormapName) {
            colormapName = cmap;
            colormap = controller->getColormap(colormapName);
            setupControls();
        }


        float px, py;
        getPixel(px,py);

        colorX = 0.0, colorScaleX = 50.0*px;
        colorY = 0.0, colorScaleY = 1.0;

        int n = ColorMap::sizeArray/3;

        float rgb[3];

        glBegin(GL_TRIANGLE_STRIP);

        for(int i=0;i<=n;i++) {
            float y =  (float) i/(float) n;
            controller->getColor(colormapName, 1 - y, rgb);
            glColor3fv(rgb);
            glVertex2f(colorX               ,colorY + colorScaleY*(1-y));
            glVertex2f(colorX + colorScaleX ,colorY + colorScaleY*(1-y));
        }
        glEnd();


        glColor4f(0,0,0,0.9);
        setFont("Helvetica", 12, "bold");
        renderText(colorX, colorY - 20*py , (char*) nodeProperty.c_str()); // which property mapped to legend

        for(int i=0;i<=10;i++) {
            float y =  (float) i/10.0;
            float v =  getValue(1 - y);
            char str[20];
            sprintf(str, "%.4f", v);
            renderText(colorX - 5.0*px, colorY + colorScaleY*(1 - y), str, "right", "middle");
        }

        glColor4f(0,0,0,0.8);

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glRectf(colorX,colorY,colorX+colorScaleX,colorY+colorScaleY);

        //float y = 1 - thresholdImportance;
        //glRectf(-0.01, bottomy + (topy-bottomy)*( y-dy/2), h+0.01, bottomy + (topy-bottomy)*(y+dy/2));

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    // I/O
    virtual void save(FILE *fp) {
        if(!colormap) return;
        fprintf(fp, "Type\t%d\n", colormap->type);
        fprintf(fp, "NumColors\t%d\n", (int) colormap->colors.size());
        for(unsigned int i = 0; i < colormap->colors.size(); i++) {
            fprintf(fp, "%f %f %f %f\n",
                    colormap->colors[i]->t,
                    colormap->colors[i]->r,
                    colormap->colors[i]->g,
                    colormap->colors[i]->b);
        }
    }
    virtual void load(vector<string> custom) {
        assert(controller);
        if(custom.size()<2) return;
        int type;
        int colors;
        sscanf(custom[0].c_str(), "Type\t%d", &type);
        sscanf(custom[1].c_str(), "NumColors\t%d", &colors);
        colormap = new ColorMap(type);
        for(int i=0;i<colors && i+2<(int) custom.size();i++) {
            string str = custom[i+2];
            float t, r, g, b;
            sscanf(str.c_str(), "%f %f %f %f\n", &t, &r, &g, &b);
            colormap->insert(t,r,g,b);
        }
        colormap->generate();
        string cmapName = this->getName()+".colormap";

        string mappedCmap = getPropertyString("colormap");
	print("Adding color map %s\n", cmapName.c_str());
        controller->addColormap(cmapName.c_str(), colormap);
        controller->registerProperty("colormap", cmapName.c_str());
        setupControls();
    }
};

class NodeLegend: public Legend {
public:
    NodeLegend(const char *title):Legend(title, "node") {
    }
};
class EdgeLegend: public Legend {
public:
    EdgeLegend(const char *title):Legend(title, "edge") {
    }
};

ColorControl::ColorControl(ColorItem *node, ColorMap *tf, Legend *editor):Clickable() {
    x = 0.1;
    y = 0.1;
    this->editor = editor;
    this->node = node;
    this->tf = tf;
}
void ColorControl::draw() {
    if(!editor->isSelected()) return;


    float px, py;
    float tempx, tempy;
    editor->getPixelWindow(tempx,tempy);
    editor->toViewDisplacement(tempx, tempy, px, py);

    float textw1, texth1;
    float textw2, texth2;
    editor->getTextBounds(textw1, texth1, (char*) "Edit");
    editor->getTextBounds(textw2, texth2, (char*) "Delete");

    h = 10.0*py;
    x = 0.0;
    y = editor->colorY + editor->colorScaleY*node->t - 0.5*h;

    //glRectf(x,y,x+w, y+h);

    float x1 = editor->colorX - 2.0*px;
    float w0 = editor->colorScaleX + 4.0*px;
    float x0 = x1 + 0.5*w0;
    float y0 = y; //editor->colorY;
    float h0 = h; //editor->colorScaleY;

    // coordinates of buttons "Edit" and "Delete". Todo: generalize this
    button1x = 5.0*px + w0;
    button1y = y;
    button1w = textw1 + 5.0*px;
    button1h = texth1 + 5.0*py;

    button2x = 5.0*px + button1x + button1w;
    button2y = y;
    button2w = textw2 + 5.0*px;
    button2h = texth2 + 5.0*py;

    w = button2x + button2w - x;

    float alpha = (selected)? 0.75: 0.25;

    glColor4f(0.0,0.0,0.0,1.0);

    if(selected) {
        glBegin(GL_LINES);
        {
            glVertex2f(x0 + 0.5*w0, y0 + h0);
            glVertex2f(x0 + 0.5*w0 + button1w + button2w + 10.0*px, y0 + 0.5*h0);
        }
        glEnd();
        glColor4f(0.0,0.0,0.0,1.0);
        editor->renderTextBox(button1x, button1y, (char*) "Edit", "left", "bottom", 2.0*px, 0.5*py);
        editor->renderTextBox(button2x, button2y, (char*) "Delete", "left", "bottom", 2.0*px, 0.5*py);
        char valueStr[50];
        sprintf(valueStr, "%.03f", editor->getValue(node->t));
        editor->renderText(button1x, button1y + 1.0*button1h, valueStr, "left", "middle");
    }


    glColor4f(1,1,1,alpha);
    glLineWidth(2);
    //glRectf(x,y,x+w,y+h);

    glLineWidth(3);

    glBegin(GL_LINE_LOOP);
    {
        glVertex2f(x0-0.5*w0, y0);
        glVertex2f(x0+0.5*w0, y0);
        glVertex2f(x0+0.5*w0, y0+h0);
        glVertex2f(x0-0.5*w0, y0+h0);
    }
    glEnd();


    glLineWidth(1);
    glColor4f(0.0,0.0,0.0,alpha);
    glBegin(GL_LINE_LOOP);
    {
        glVertex2f(x0-0.5*w0, y0);
        glVertex2f(x0+0.5*w0, y0);
        glVertex2f(x0+0.5*w0, y0+h0);
        glVertex2f(x0-0.5*w0, y0+h0);
    }
    glEnd();

    /*
  float xclose = x0-w0 + 1*px;
  float wclose = w - 2*px;
  float yclose = editor->colorControlY + 1*py;
  float hclose = editor->colorControlScaleY - 2*py;
  glColor4f(1,1,1,0.5);
  glBegin(GL_QUADS);
  {
  glVertex2f(xclose, yclose);
  glVertex2f(xclose, yclose+hclose);
  glVertex2f(xclose+wclose, yclose+hclose);
  glVertex2f(xclose+wclose, yclose);

  }

  glEnd();
  */
}

void ColorControl::drag(float , float , float /*dx*/, float dy, int /*modifiers*/) { 
    y+=dy;
    if(y<editor->colorY) y = editor->colorY;
    if(y>=editor->colorY + editor->colorScaleY) y = editor->colorY + editor->colorScaleY - 0.5/(float) tf->size;
    node->t = (y + 0.5*h - editor->colorY)/editor->colorScaleY;
    tf->generate();
}

void ColorControl::click(float x , float y, int modifiers) {
    if((x >=button1x && x<button1x+button1w ) ||
            modifiers & NZ_ACTIVE_SHIFT) {
        Clickable::click(x,y, modifiers);
        float rgb[3];
        this->setSelected(false);
        bool valid = queryColor(rgb);
        if(valid) {
            node->r = rgb[0];
            node->g = rgb[1];
            node->b = rgb[2];
            tf->generate();
        }
    } else if(x>=button2x && x<button2x+button2w) {
        editor->deleteColorControl(this);
        return;
    } else {
        Clickable::click(x,y, modifiers);
    }
}

View *createNodeLegend(string name) {
    NodeLegend *legend1 = new NodeLegend(name.c_str());
    legend1->setLocation(0.6, 0.1);
    legend1->setSize(0.4, 0.8);
    legend1->setVisible(true);
    legend1->addCloseButton();
    legend1->addTransparentButton();
    return legend1;
}
View *createEdgeLegend(string name) {
    EdgeLegend *legend1 = new EdgeLegend(name.c_str());
    legend1->setLocation(0.6, 0.1);
    legend1->setSize(0.4, 0.8);
    legend1->setVisible(true);
    legend1->addCloseButton();
    legend1->addTransparentButton();
    return legend1;
}


